package client;

import tools.*;

import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;

public class ClientControl implements Runnable
{
	String host;
	int port;
	ObjectOutputStream out;
	ObjectInputStream in;	
	ClientBadugi client;
	Socket socket;
	GameState gameState;
	/*
	 * id klienta nadany przez serwer
	 * */
	int id;
	
	
	
	ClientControl(String host, int port, ClientBadugi client) throws Exception
	{
		this.host = host;
		this.port = port;
		this.client = client;
      try
      {
      	socket = new Socket(this.host, this.port);
      	/** pobranie strumieni do pisania i czytania w trakcie komunikacji z serwerem */
    	  
    	  	out = new ObjectOutputStream(socket.getOutputStream());
    	  	in = new ObjectInputStream(socket.getInputStream());
    	  
      }
      catch(IOException e)
      {
         System.out.println("Blad przy inicjalizacji komunikacji z serwerem: " + this.host);
         throw e;
      }
      
      sendResponse(Protocol.HELLO);
      
      Protocol p = readMessage();
      
      if (p == Protocol.NO_PLACE)
      {
      	throw new Exception("Brak miejsc");
      }
      else if (p == Protocol.WELCOME)
      {
      	client.setCommunication("Your ID: "+ this.id);
      }
      else
      	throw new Exception("Otrzymano od serwera niezaimplementowana komende.");	
	}

   synchronized private void sendResponse(Protocol p)
   {
	   Message m = new Message(p, this.id, gameState);
	   try
	   {
	   	out.reset();
		   out.writeObject(m);
		   client.setCommunication("Client: " + m.p);
	   }
	   catch(IOException e)
	   {
		   System.out.println("Blad przy wysylaniu komunikatu [" + p + "] do serwera: " + this.host);
	   }
   }
   
   private Protocol readMessage()
   {
   	  	
   	Message message;
   	try
   	{
   		/** odczyt wiadomosci od serwera */
     	 	message = (Message) in.readObject();
   		
     	 	if (message.gameState != null) 
     	 	{
     	 		gameState = message.gameState;
     	 		this.id = message.clientID;
     	 	   client.setCommunication("Server: " + message.p + ". Total players: "+ gameState.countPlayers());
     	 	   
     	 	   if (message.p == Protocol.KICK)
     	 	   {
     	 	   	gameState = null;
     	 	   	this.id = 0;
     	 	   }
     	 	}
     	 	else
     	 		System.out.println("ClientControl.readMessage() - gameSate.message is null (" + id +")");
     	 	
     	 	//System.out.println(message.p + " - wiadomosc od serwera: " + this.host);
     	 	return message.p;
      }
      catch (IOException | ClassNotFoundException e) 
      {
      	System.out.println(e.getMessage() +". Blad komunikacji z serwerem: " + this.host);
         return Protocol.QUIT;
      }
   }
	
	@Override
	public void run()
	{
		Protocol protocol;
		boolean sendQuit;
	
		while(true)
		{
			protocol = readMessage();
			
			if(protocol == Protocol.QUIT)
			{
				sendQuit = false;
				break;  // quit oznacza serwer wyszedl - informuje ze zakonczyl polaczenie
			}
			
			/* usuniecie z serwera za brak kasy */
			if(protocol == Protocol.KICK)
			{
				sendQuit = true;
				break; // kick oznacza wymuszony quit - wysylamy quit
			}
			
			/* licytuj kliencie  */
			if(protocol == Protocol.BID)
			{
				Player player = this.getPlayer();
				
				/* jesli jest tryb Bota to oddaj jemu podjecie decyzji */
				if (this.client.isBotMode())
				{
					this.client.getBot().makeDecision(this.gameState.getHighestBetInRound(), this.gameState.getNumberOfRound(), player);
				}
				else
				{
				/* w przeciwnym przypadku wlacz licytacje. procedura licytacji wysle */
					this.client.getButtonPanel().setButtonsToBid(player.getInGameChips(), this.gameState.getHighestBetInRound(), player.getTotalChips());
					/* tutaj nastepuje wlaczenie przyciskow na oknie ktore umozliwia klientowi postawienie zakladu 
					 * Po przycisnieciu przycisku na panelu nastapi wywolanie odpowiedniej metody z klasy ClientControl
					 *  
					 * */

				}
			}
			
			if(protocol == Protocol.EXCHANGE_CARDS)
			{
				/* jesli jest tryb Bota to oddaj jemu podjecie decyzji */
				if (this.client.isBotMode())
				{
					Player player = this.getPlayer();
					this.client.getBot().exchangeCards(player);
				}
				else
				{
					/* wlacz wymiane kart. procedura wyslij karty */
					this.client.getButtonPanel().setOnExchangeCards();
					/*
					 * teraz nastapi wlaczenie przyciskow umozliwiajacych wymiane kart
					 * Po wyborze przez uzytkownika nastapi wywolanie odpowiedniej metody z klasy ClientControl
					 * */
				}			
			}
			if(protocol == Protocol.REFRESH)
			{
				/* odswiez panele nic nie trzeba robic i tak po kazdym odbiorze komunikatu odswieza sie panel */
				
			}
			
			if(protocol == Protocol.START_GAME)
			{
				/* odswiez panele nic nie trzeba robic i tak po kazdym odbiorze komunikatu odswieza sie panel */
				
			}

			if(protocol == Protocol.END_GAME)
			{
				/* jesli w trakcie mojego ruchu wyszli inni gracze to nie wylacza sie automatycznie przyciski a gra sie zakonczy */
				client.getButtonPanel().setOffBidButtons();
			}
			
			client.refreshPanels();
		}

		closeConnection(sendQuit);
		client.refreshPanels();
	}

   void closeConnection(boolean withSendQuit)
   {
   	try 
	   {
   		if (withSendQuit) sendResponse(Protocol.QUIT);
   		in.close();
			out.close();
			socket.close();
			client.closeConnection();
			System.out.println("Koniec polaczenia z serwerem: " + this.host);
		}
		catch (IOException e) 
		{
			System.out.println("Blad przy zamknieciu polaczenia z serwerem: " + this.host);
			return;
		}
   }
   
   Player getPlayer()
   {
   	for(Player player : this.gameState.players)
   		if (player.getID() == this.id)
   			return player;
   	return null; 
   }

	void check()
	{
		Player player = this.getPlayer();
		player.check();
		sendResponse(Protocol.CHECK);
	}

	void bet(int bet)
	{
		Player player = this.getPlayer();
		if (player.getTotalChips() == bet)
		{
			this.allin();		
		}
		else
		{
			player.bet(bet);
			sendResponse(Protocol.BET);
		}
	}

	void raise(int bet)
	{
		Player player = this.getPlayer();
		if (player.getTotalChips() == bet)
		{
			this.allin();
		}
		else
		{
			player.raise(bet);
			sendResponse(Protocol.RAISE);
		}
	}

	void call()
	{
		Player player = this.getPlayer();
		int bet = this.gameState.getHighestBetInRound() - player.getInGameChips();
		if (player.getTotalChips() == bet)
		{
			this.allin();
		}
		else
		{
			player.call(bet);
			sendResponse(Protocol.CALL);
		}
	}

	void fold()
	{
		Player player = this.getPlayer();
		player.fold();
		sendResponse(Protocol.FOLD);
	}

	void allin()
	{
		Player player = this.getPlayer();
		player.allIn();
		sendResponse(Protocol.ALL_IN);
	}

	void exchangeCards(ArrayList<Card> cards)
	{
		Player player = this.getPlayer();
		player.exchangeCards(cards);
		sendResponse(Protocol.EXCHANGED_CARDS);
	}

	void dontExchange()
	{
		sendResponse(Protocol.NO_EXCHANGE);
	}
}
